🎓 نظام حضور الطلاب - Bright Minds

جاهز للعمل

📷 امسح QR Code هنا

👇 اضغط الزر لتشغيل الكاميرا

📋 آخر العمليات اليوم

✅ الحضور

0

❌ الغياب

0

⏰ المتأخرين

0

🏫 داخل المدرسة

0

⏰ أوقات الدوام

🔧 إعدادات عامة

📱 إعدادات WhatsApp (اختياري)

💡 لتفعيل WhatsApp، تحتاج إعداد Firebase Functions مع Twilio API

🔥 إعدادات Firebase (حفظ سحابي)

📌 كيف أحصل على Firebase Config؟
  1. اذهب: console.firebase.google.com
  2. اضغط "Add project" → اسم: Bright-Minds
  3. فعّل Firestore Database → اختر Test mode
  4. Project Settings ⚙️ → Web App → انسخ المعلومات ⬇️
💡 فوائد Firebase:
  • تخزين دائم - لا يُحذف أبداً
  • نسخ احتياطي تلقائي
  • مزامنة بين الأجهزة
  • تقارير سنوية محفوظة

🔔 إعدادات الإشعارات (بديل WhatsApp)

💡 ما هي Push Notifications؟

إشعارات فورية تظهر على الموبايل/الكمبيوتر عند دخول/خروج الطالب

✅ مجانية 100%
• لا تحتاج API خارجي
• لا تحتاج WhatsApp
• تصل فوراً

🎓 Bright Minds

${student.name}

${canvas.outerHTML}

الصف: ${student.grade} - ${student.section}

رقم الطالب: ${student.studentId}

يرجى المحافظة على هذه البطاقة

`); printWindow.document.close(); setTimeout(() => { printWindow.print(); }, 500); } function loadStudents() { const list = document.getElementById('students-list'); list.innerHTML = ''; if (students.length === 0) { list.innerHTML = '

لا يوجد طلاب. أضف طالب جديد للبدء.

'; return; } students.forEach(student => { const card = document.createElement('div'); card.className = 'student-card'; const photoHTML = student.photo ? `${student.name}` : '
👤
'; card.innerHTML = ` ${photoHTML}

${student.name}

📚 ${student.grade} - ${student.section}

🆔 ${student.studentId}

📱 ${student.parentPhone}

📅 ${new Date(student.createdAt).toLocaleDateString('ar-IQ')}

`; list.appendChild(card); }); } function searchStudents(e) { const searchTerm = e.target.value.toLowerCase(); const filtered = students.filter(s => s.name.toLowerCase().includes(searchTerm) || s.studentId.toLowerCase().includes(searchTerm) || s.grade.toLowerCase().includes(searchTerm) ); const list = document.getElementById('students-list'); list.innerHTML = ''; if (filtered.length === 0) { list.innerHTML = '

لم يتم العثور على نتائج

'; return; } filtered.forEach(student => { const card = document.createElement('div'); card.className = 'student-card'; const photoHTML = student.photo ? `${student.name}` : '
👤
'; card.innerHTML = ` ${photoHTML}

${student.name}

📚 ${student.grade} - ${student.section}

🆔 ${student.studentId}

📱 ${student.parentPhone}

`; list.appendChild(card); }); } function viewStudentQR(studentId) { const student = students.find(s => s.studentId === studentId); if (student) { generateQRCode(student); document.getElementById('student-modal').classList.remove('hidden'); } } function deleteStudent(studentId) { if (confirm('هل أنت متأكد من حذف هذا الطالب؟')) { students = students.filter(s => s.studentId !== studentId); saveToLocalStorage(); showNotification('✅ تم حذف الطالب', 'success'); loadStudents(); } } function exportStudents() { if (students.length === 0) { showNotification('لا يوجد طلاب للتصدير', 'error'); return; } const data = students.map(s => ({ 'رقم الطالب': s.studentId, 'الاسم': s.name, 'الصف': s.grade, 'الشعبة': s.section, 'رقم ولي الأمر': s.parentPhone, 'تاريخ الإضافة': new Date(s.createdAt).toLocaleDateString('ar-IQ') })); const ws = XLSX.utils.json_to_sheet(data); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'الطلاب'); XLSX.writeFile(wb, `قائمة_الطلاب_${new Date().toISOString().split('T')[0]}.xlsx`); showNotification('✅ تم تصدير قائمة الطلاب', 'success'); } // ==================== REPORTS ==================== function loadTodayStats() { const today = new Date().toISOString().split('T')[0]; const todayStatus = dailyStatus.filter(d => d.date === today); const presentStudents = todayStatus.length; const insideSchool = todayStatus.filter(s => s.lastAction === 'entry').length; const totalStudents = students.length; const absentStudents = totalStudents - todayStatus.length; const [schoolStartHour, schoolStartMinute] = settings.schoolStart.split(':').map(Number); const lateStudents = todayStatus.filter(s => { if (s.firstEntry) { const entryTime = new Date(s.firstEntry); const entryMinutes = entryTime.getHours() * 60 + entryTime.getMinutes(); const schoolStartMinutes = schoolStartHour * 60 + schoolStartMinute + settings.lateMinutes; return entryMinutes > schoolStartMinutes; } return false; }).length; document.getElementById('present-count').textContent = presentStudents; document.getElementById('absent-count').textContent = absentStudents; document.getElementById('late-count').textContent = lateStudents; document.getElementById('inside-count').textContent = insideSchool; } function generateReport() { const date = document.getElementById('report-date').value; const grade = document.getElementById('report-grade').value; if (!date) { showNotification('اختر التاريخ أولاً', 'error'); return; } let reportData = dailyStatus.filter(d => d.date === date); if (grade) { reportData = reportData.filter(d => { const student = students.find(s => s.studentId === d.studentId); return student && student.grade === grade; }); } const allStudentsForReport = grade ? students.filter(s => s.grade === grade) : students; const tableContainer = document.getElementById('report-table'); if (allStudentsForReport.length === 0) { tableContainer.innerHTML = '

لا يوجد طلاب

'; return; } let tableHTML = ` `; allStudentsForReport.forEach(student => { const status = reportData.find(d => d.studentId === student.studentId); let statusText = '❌ غائب'; let entryTime = '-'; let exitTime = '-'; let movements = 0; if (status) { const [schoolStartHour, schoolStartMinute] = settings.schoolStart.split(':').map(Number); let isLate = false; if (status.firstEntry) { const entryDate = new Date(status.firstEntry); const entryMinutes = entryDate.getHours() * 60 + entryDate.getMinutes(); const schoolStartMinutes = schoolStartHour * 60 + schoolStartMinute + settings.lateMinutes; isLate = entryMinutes > schoolStartMinutes; entryTime = entryDate.toLocaleTimeString('ar-IQ', { hour: '2-digit', minute: '2-digit' }); } if (status.lastExit) { const exitDate = new Date(status.lastExit); exitTime = exitDate.toLocaleTimeString('ar-IQ', { hour: '2-digit', minute: '2-digit' }); } statusText = isLate ? '⏰ متأخر' : '✅ حاضر'; movements = status.movements; } tableHTML += ` `; }); tableHTML += `
الاسم الصف الشعبة الحالة وقت الدخول وقت الخروج عدد الحركات
${student.name} ${student.grade} ${student.section} ${statusText} ${entryTime} ${exitTime} ${movements}
`; tableContainer.innerHTML = tableHTML; } function exportReport() { const date = document.getElementById('report-date').value; const grade = document.getElementById('report-grade').value; if (!date) { showNotification('اختر التاريخ أولاً', 'error'); return; } let reportData = dailyStatus.filter(d => d.date === date); if (grade) { reportData = reportData.filter(d => { const student = students.find(s => s.studentId === d.studentId); return student && student.grade === grade; }); } const allStudentsForReport = grade ? students.filter(s => s.grade === grade) : students; const data = allStudentsForReport.map(student => { const status = reportData.find(d => d.studentId === student.studentId); let statusText = 'غائب'; let entryTime = '-'; let exitTime = '-'; let movements = 0; if (status) { const [schoolStartHour, schoolStartMinute] = settings.schoolStart.split(':').map(Number); let isLate = false; if (status.firstEntry) { const entryDate = new Date(status.firstEntry); const entryMinutes = entryDate.getHours() * 60 + entryDate.getMinutes(); const schoolStartMinutes = schoolStartHour * 60 + schoolStartMinute + settings.lateMinutes; isLate = entryMinutes > schoolStartMinutes; entryTime = entryDate.toLocaleTimeString('ar-IQ', { hour: '2-digit', minute: '2-digit' }); } if (status.lastExit) { const exitDate = new Date(status.lastExit); exitTime = exitDate.toLocaleTimeString('ar-IQ', { hour: '2-digit', minute: '2-digit' }); } statusText = isLate ? 'متأخر' : 'حاضر'; movements = status.movements; } return { 'الاسم': student.name, 'الصف': student.grade, 'الشعبة': student.section, 'الحالة': statusText, 'وقت الدخول': entryTime, 'وقت الخروج': exitTime, 'عدد الحركات': movements }; }); const ws = XLSX.utils.json_to_sheet(data); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'تقرير الحضور'); XLSX.writeFile(wb, `تقرير_الحضور_${date}.xlsx`); showNotification('✅ تم تصدير التقرير', 'success'); } function loadGrades() { const gradeSelect = document.getElementById('report-grade'); const uniqueGrades = [...new Set(students.map(s => s.grade))]; gradeSelect.innerHTML = ''; uniqueGrades.forEach(grade => { const option = document.createElement('option'); option.value = grade; option.textContent = grade; gradeSelect.appendChild(option); }); } // ==================== SETTINGS ==================== function loadSettings() { document.getElementById('school-start').value = settings.schoolStart; document.getElementById('school-end').value = settings.schoolEnd; document.getElementById('late-minutes').value = settings.lateMinutes; document.getElementById('auto-exit').checked = settings.autoExit; document.getElementById('show-photo').checked = settings.showPhoto; document.getElementById('sound-enabled').checked = settings.soundEnabled; document.getElementById('whatsapp-enabled').checked = settings.whatsappEnabled; document.getElementById('firebase-function-url').value = settings.firebaseFunctionUrl; } function saveSettings() { settings.schoolStart = document.getElementById('school-start').value; settings.schoolEnd = document.getElementById('school-end').value; settings.lateMinutes = parseInt(document.getElementById('late-minutes').value); settings.autoExit = document.getElementById('auto-exit').checked; settings.showPhoto = document.getElementById('show-photo').checked; settings.soundEnabled = document.getElementById('sound-enabled').checked; settings.whatsappEnabled = document.getElementById('whatsapp-enabled').checked; settings.firebaseFunctionUrl = document.getElementById('firebase-function-url').value; saveToLocalStorage(); showNotification('✅ تم حفظ الإعدادات', 'success'); } // ==================== UTILITIES ==================== function showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); } // Add sample students for demo if (students.length === 0) { students = [ { studentId: 'S001', name: 'أحمد محمد علي', grade: 'الخامس', section: 'أ', parentPhone: '+9647700000001', photo: null, qrCode: 'S001', active: true, createdAt: new Date().toISOString() }, { studentId: 'S002', name: 'فاطمة حسن خالد', grade: 'الرابع', section: 'ب', parentPhone: '+9647700000002', photo: null, qrCode: 'S002', active: true, createdAt: new Date().toISOString() }, { studentId: 'S003', name: 'علي أحمد سعيد', grade: 'السادس', section: 'أ', parentPhone: '+9647700000003', photo: null, qrCode: 'S003', active: true, createdAt: new Date().toISOString() } ]; saveToLocalStorage(); } // ==================== FIREBASE & NOTIFICATIONS ==================== // إضافة متغيرات Firebase var db = null; var firebaseInitialized = false; var notificationPermission = 'default'; // إضافة خصائص Firebase لـ settings (function() { if (typeof settings !== 'undefined' && !settings.hasOwnProperty('firebaseEnabled')) { settings.firebaseEnabled = false; settings.firebaseApiKey = ''; settings.firebaseAuthDomain = ''; settings.firebaseProjectId = ''; settings.firebaseStorageBucket = ''; settings.firebaseMessagingSender = ''; settings.firebaseAppId = ''; settings.pushNotificationsEnabled = false; } })(); // تهيئة Firebase function initializeFirebase() { if (firebaseInitialized) { console.log('Firebase already initialized'); return true; } if (!settings.firebaseEnabled) { console.log('Firebase not enabled'); return false; } var config = { apiKey: settings.firebaseApiKey || '', authDomain: settings.firebaseAuthDomain || '', projectId: settings.firebaseProjectId || '', storageBucket: settings.firebaseStorageBucket || '', messagingSenderId: settings.firebaseMessagingSender || '', appId: settings.firebaseAppId || '' }; if (!config.apiKey || !config.projectId) { console.log('Firebase config incomplete'); showNotification('⚠️ Firebase: المعلومات غير مكتملة', 'warning'); return false; } try { firebase.initializeApp(config); db = firebase.firestore(); firebaseInitialized = true; console.log('✅ Firebase initialized successfully'); showNotification('✅ تم الاتصال بـ Firebase بنجاح!', 'success'); return true; } catch (error) { console.error('Firebase initialization error:', error); showNotification('❌ خطأ في Firebase: ' + error.message, 'error'); return false; } } // حفظ في Firestore function saveToFirestore(record) { if (!settings.firebaseEnabled || !firebaseInitialized || !db) { return; } try { var now = new Date(); var year = now.getMonth() >= 8 ? now.getFullYear() : now.getFullYear() - 1; record.schoolYear = year + '-' + (year + 1); db.collection('attendance').add(record) .then(function() { console.log('✅ Saved to Firebase:', record.attendanceId); }) .catch(function(error) { console.error('Firebase save error:', error); }); } catch (error) { console.error('saveToFirestore error:', error); } } // طلب إذن الإشعارات function requestNotificationPermission() { if (!('Notification' in window)) { showNotification('⚠️ المتصفح لا يدعم الإشعارات', 'warning'); return; } Notification.requestPermission().then(function(permission) { notificationPermission = permission; var statusDiv = document.getElementById('notification-status'); if (statusDiv) { statusDiv.style.display = 'block'; if (permission === 'granted') { statusDiv.style.background = '#d4edda'; statusDiv.style.color = '#155724'; statusDiv.style.border = '2px solid #c3e6cb'; statusDiv.innerHTML = '✅ تم السماح بالإشعارات! ستصل الإشعارات الآن'; showNotification('✅ تم تفعيل الإشعارات بنجاح', 'success'); } else { statusDiv.style.background = '#f8d7da'; statusDiv.style.color = '#721c24'; statusDiv.style.border = '2px solid #f5c6cb'; statusDiv.innerHTML = '❌ تم رفض الإشعارات. يرجى السماح من إعدادات المتصفح'; } } }).catch(function(error) { console.error('Notification permission error:', error); }); } // إرسال إشعار function sendPushNotification(studentName, type, time) { if (!settings.pushNotificationsEnabled) return; if (notificationPermission !== 'granted') return; try { var title = '🏫 نظام الحضور - Bright Minds'; var body = type === 'entry' ? '✅ ' + studentName + ' دخل المدرسة\nالساعة: ' + time : '🚪 ' + studentName + ' خرج من المدرسة\nالساعة: ' + time; var notification = new Notification(title, { body: body, icon: 'data:image/svg+xml,🏫', badge: 'data:image/svg+xml,🔔', tag: 'attendance-' + Date.now(), requireInteraction: false }); setTimeout(function() { notification.close(); }, 10000); notification.onclick = function() { window.focus(); notification.close(); }; console.log('✅ Push notification sent'); } catch (error) { console.error('Push notification error:', error); } } // تعديل processAttendance لإضافة Firebase و Notifications (function() { var originalProcessAttendance = window.processAttendance; window.processAttendance = function(studentId) { var student = students.find(function(s) { return s.studentId === studentId; }); if (!student) { showNotification('❌ طالب غير موجود', 'error'); playErrorSound(); return; } if (!student.active) { showNotification('⚠️ ' + student.name + ' غير نشط', 'error'); playErrorSound(); return; } var today = new Date().toISOString().split('T')[0]; var dailyStatusId = studentId + '_' + today; var status = dailyStatus.find(function(d) { return d.id === dailyStatusId; }); var attendanceType; var movements = 1; if (status) { attendanceType = status.lastAction === 'entry' ? 'exit' : 'entry'; movements = (status.movements || 1) + 1; status.lastAction = attendanceType; status.movements = movements; if (attendanceType === 'exit') { status.lastExit = new Date().toISOString(); } } else { attendanceType = 'entry'; status = { id: dailyStatusId, studentId: studentId, date: today, lastAction: 'entry', movements: 1, firstEntry: new Date().toISOString() }; dailyStatus.push(status); } var attendanceRecord = { attendanceId: studentId + '_' + Date.now(), studentId: student.studentId, studentName: student.name, grade: student.grade, section: student.section, type: attendanceType, timestamp: new Date().toISOString(), date: today, deviceId: deviceId }; attendanceRecords.push(attendanceRecord); saveToLocalStorage(); // حفظ في Firebase if (settings.firebaseEnabled && firebaseInitialized) { saveToFirestore(attendanceRecord); } displayScanResult(student, attendanceType); playSound(attendanceType); addToRecentScans(student, attendanceType); loadTodayStats(); // إرسال إشعار var timeStr = new Date().toLocaleTimeString('ar-IQ', { hour: '2-digit', minute: '2-digit' }); sendPushNotification(student.name, attendanceType, timeStr); // WhatsApp إذا كان مفعّل if (settings.whatsappEnabled && settings.firebaseFunctionUrl) { sendWhatsAppNotification(student, attendanceType); } }; })(); // تعديل saveSettings (function() { var originalSaveSettings = window.saveSettings; window.saveSettings = function() { settings.schoolStart = document.getElementById('school-start').value; settings.schoolEnd = document.getElementById('school-end').value; settings.lateMinutes = parseInt(document.getElementById('late-minutes').value); settings.autoExit = document.getElementById('auto-exit').checked; settings.showPhoto = document.getElementById('show-photo').checked; settings.soundEnabled = document.getElementById('sound-enabled').checked; settings.whatsappEnabled = document.getElementById('whatsapp-enabled').checked; settings.firebaseFunctionUrl = document.getElementById('firebase-function-url').value; // Firebase settings var wasEnabled = settings.firebaseEnabled; var fbApiKey = document.getElementById('firebase-api-key'); var fbAuthDomain = document.getElementById('firebase-auth-domain'); var fbProjectId = document.getElementById('firebase-project-id'); var fbStorageBucket = document.getElementById('firebase-storage-bucket'); var fbMessagingSender = document.getElementById('firebase-messaging-sender'); var fbAppId = document.getElementById('firebase-app-id'); var fbEnabled = document.getElementById('firebase-enabled'); var pushEnabled = document.getElementById('push-notifications-enabled'); if (fbApiKey) settings.firebaseApiKey = fbApiKey.value.trim(); if (fbAuthDomain) settings.firebaseAuthDomain = fbAuthDomain.value.trim(); if (fbProjectId) settings.firebaseProjectId = fbProjectId.value.trim(); if (fbStorageBucket) settings.firebaseStorageBucket = fbStorageBucket.value.trim(); if (fbMessagingSender) settings.firebaseMessagingSender = fbMessagingSender.value.trim(); if (fbAppId) settings.firebaseAppId = fbAppId.value.trim(); if (fbEnabled) settings.firebaseEnabled = fbEnabled.checked; if (pushEnabled) settings.pushNotificationsEnabled = pushEnabled.checked; saveToLocalStorage(); // تفعيل Firebase if (settings.firebaseEnabled && !wasEnabled) { setTimeout(function() { var initialized = initializeFirebase(); if (initialized) { showNotification('✅ تم حفظ الإعدادات وتفعيل Firebase', 'success'); } else { showNotification('⚠️ تم الحفظ - تأكد من Firebase Config', 'warning'); } }, 500); } else { showNotification('✅ تم حفظ الإعدادات', 'success'); } }; })(); // تعديل loadSettings (function() { var originalLoadSettings = window.loadSettings; window.loadSettings = function() { document.getElementById('school-start').value = settings.schoolStart; document.getElementById('school-end').value = settings.schoolEnd; document.getElementById('late-minutes').value = settings.lateMinutes; document.getElementById('auto-exit').checked = settings.autoExit; document.getElementById('show-photo').checked = settings.showPhoto; document.getElementById('sound-enabled').checked = settings.soundEnabled; document.getElementById('whatsapp-enabled').checked = settings.whatsappEnabled; document.getElementById('firebase-function-url').value = settings.firebaseFunctionUrl; // Load Firebase settings var fbApiKey = document.getElementById('firebase-api-key'); var fbAuthDomain = document.getElementById('firebase-auth-domain'); var fbProjectId = document.getElementById('firebase-project-id'); var fbStorageBucket = document.getElementById('firebase-storage-bucket'); var fbMessagingSender = document.getElementById('firebase-messaging-sender'); var fbAppId = document.getElementById('firebase-app-id'); var fbEnabled = document.getElementById('firebase-enabled'); var pushEnabled = document.getElementById('push-notifications-enabled'); if (fbApiKey) fbApiKey.value = settings.firebaseApiKey || ''; if (fbAuthDomain) fbAuthDomain.value = settings.firebaseAuthDomain || ''; if (fbProjectId) fbProjectId.value = settings.firebaseProjectId || ''; if (fbStorageBucket) fbStorageBucket.value = settings.firebaseStorageBucket || ''; if (fbMessagingSender) fbMessagingSender.value = settings.firebaseMessagingSender || ''; if (fbAppId) fbAppId.value = settings.firebaseAppId || ''; if (fbEnabled) fbEnabled.checked = settings.firebaseEnabled || false; if (pushEnabled) pushEnabled.checked = settings.pushNotificationsEnabled || false; // تفعيل Firebase if (settings.firebaseEnabled) { setTimeout(function() { initializeFirebase(); }, 1000); } // التحقق من إذن الإشعارات if ('Notification' in window && Notification.permission === 'granted') { var statusDiv = document.getElementById('notification-status'); if (statusDiv) { statusDiv.style.display = 'block'; statusDiv.style.background = '#d4edda'; statusDiv.style.color = '#155724'; statusDiv.innerHTML = '✅ الإشعارات مفعّلة ومسموحة'; } } }; })(); // إضافة event listener لزر الإشعارات window.addEventListener('load', function() { setTimeout(function() { var notifBtn = document.getElementById('request-notification-permission'); if (notifBtn) { notifBtn.addEventListener('click', function(e) { e.preventDefault(); requestNotificationPermission(); }); console.log('✅ Notification button listener added'); } }, 1000); });